package net.avcompris.binding;

import java.lang.reflect.Method;

import com.avcompris.common.annotation.Nullable;
import com.google.common.collect.ImmutableSet;

/**
 * super interface for binders.
 * 
 * @author David Andrianavalontsalama
 */
public interface Binder<U> {

	/**
	 * bidirectly bind a Java dynamic proxy to a U-node,
	 * using the XPath expression declared as an annotation in the clazz
	 * interface.
	 */
	<T> T bind(U node, Class<T> clazz);

	/**
	 * bidirectly bind a Java dynamic proxy to a U-node,
	 * using the XPath expression declared in the configuration parameter,
	 * overriding the XPath expression, if any, declared as an annotation
	 * in the clazz interface.
	 */
	<T> T bind(BindConfiguration configuration, U node, Class<T> clazz);

	/**
	 * bidirectly bind a Java dynamic proxy to a U-node,
	 * using the XPath expression declared as an annotation in the clazz
	 * interface
	 * and the {@link ClassLoader} passed as a parameter.	 
	 */
	<T> T bind(U node, ClassLoader classLoader, Class<T> clazz);

	/**
	 * bidirectly bind a Java dynamic proxy to a U-node,
	 * using the XPath expression declared in the configuration
	 * parameter, overriding the XPath expression, if any,
	 * declared as an annotation in the clazz interface,
	 * and the {@link ClassLoader} passed as a parameter.	 
	 */
	<T> T bind(BindConfiguration configuration, U node,
			ClassLoader classLoader, Class<T> clazz);

	/**
	 * add a binding declaration for all classes: 
	 * tt>className</tt> is <tt>"*"</tt> (= all).
	 * 
	 * @param configuration the {@link BindConfiguration} to apply.
	 * If <tt>null</tt>, the existing configuration will be removed.
	 */
	void addClassBinding(@Nullable BindConfiguration configuration);

	/**
	 * add a binding declaration for a class.
	 * 
	 * @param configuration the {@link BindConfiguration} to apply.
	 * If <tt>null</tt>, the existing configuration will be removed.
	 */
	void addClassBinding(@Nullable BindConfiguration configuration,
			Class<?> clazz);

	/**
	 * add a binding declaration for a class.
	 * 
	 * @param configuration the {@link BindConfiguration} to apply.
	 * If <tt>null</tt>, the existing configuration will be removed.
	 */
	void addClassBinding(@Nullable BindConfiguration configuration,
			String className);

	/**
	 * add a binding declaration for a class.
	 */
	void addClassBinding(ClassBinding binding);

	/**
	 * get all the class bindings.
	 */
	ImmutableSet<ClassBinding> getClassBindings();

	/**
	 * add a binding declaration for a method.
	 * 
	 * @param configuration the {@link BindConfiguration} to apply.
	 * If <tt>null</tt>, the existing configuration will be removed.
	 */
	void addMethodBinding(@Nullable BindConfiguration configuration,
			Method method);

	/**
	 * add a binding declaration for a method.
	 * 
	 * @param configuration the {@link BindConfiguration} to apply.
	 * If <tt>null</tt>, the existing configuration will be removed.
	 */
	void addMethodBinding(@Nullable BindConfiguration configuration,
			Class<?> clazz, String methodName, Class<?>... paramTypes);

	/**
	 * add a binding declaration for all method names in a given class.
	 * 
	 * @param configuration the {@link BindConfiguration} to apply.
	 * If <tt>null</tt>, the existing configuration will be removed.
	 */
	void addMethodBinding(@Nullable BindConfiguration configuration,
			Class<?> clazz, Class<?>... paramTypes);

	/**
	 * add a binding declaration for a method name for all classes:
	 * <tt>className</tt> is <tt>"*"</tt> (= all).
	 * 
	 * @param configuration the {@link BindConfiguration} to apply.
	 * If <tt>null</tt>, the existing configuration will be removed.
	 */
	void addMethodBinding(@Nullable BindConfiguration configuration,
			String methodName, Class<?>... paramTypes);

	/**
	 * add a binding declaration for a method.
	 * 
	 * @param configuration the {@link BindConfiguration} to apply.
	 * If <tt>null</tt>, the existing configuration will be removed.
	 */
	void addMethodBinding(@Nullable BindConfiguration configuration,
			String className, String methodName, Class<?>... paramTypes);

	/**
	 * add a binding declaration for a method.
	 */
	void addMethodBinding(MethodBinding binding);

	/**
	 * get all the method bindings.
	 */
	ImmutableSet<MethodBinding> getMethodBindings();

	/**
	 * utility method to add bindings for methods in a given class with names
	 * <tt>getXxx()</tt>, <tt>isNullXxx()</tt> and
	 * <tt>sizeOfXxx()</tt>.
	 */
	void addFieldBinding(@Nullable BindConfiguration configuration,
			Class<?> clazz, String field);

	/**
	 * utility method to add bindings for methods in a given class with names
	 * <tt>getXxx()</tt>, <tt>isNullXxx()</tt> and
	 * <tt>sizeOfXxx()</tt>.
	 */
	void addFieldBinding(@Nullable BindConfiguration configuration,
			String className, String field);

	/**
	 * utility method to add bindings for methods with names
	 * <tt>getXxx()</tt>, <tt>isNullXxx()</tt> and
	 * <tt>sizeOfXxx()</tt> in all classes: <tt>className</tt> is <tt>"*"</tt>.
	 */
	void addFieldBinding(@Nullable BindConfiguration configuration, String field);
}
